﻿using System.Text;
using System.Text.RegularExpressions;

namespace DocumentMaking.Lib.Models;

/// <summary>
/// 源文件类
/// </summary>
public class SourceFileModel(string filePath)
{
    #region Prop

    /// <summary>
    /// 文件路径
    /// </summary>
    public string FilePath { get; set; } = filePath;

    /// <summary>
    /// 文件名
    /// </summary>
    public string FileName { get; set; } = Path.GetFileName(filePath);

    /// <summary>
    /// 编程语言
    /// </summary>
    public string Language
    {
        get
        {
            if (string.IsNullOrWhiteSpace(FileName))
                return string.Empty;
            var ext = Path.GetExtension(FileName)[1..];
            return ext switch
            {
                "cs" => "C#",
                "cpp" => "C++",
                "java" => "Java",
                "h" => "C/C++",
                "js" => "JavaScript",
                "ts" => "TypeScript",
                "py" => "Python",
                "vue" => "Vue",
                _ => ext.ToUpper()
            };
        }
    }

    /// <summary>
    /// 是否为源代码文件
    /// </summary>
    public bool IsCodeFile
    {
        get
        {
            if (string.IsNullOrWhiteSpace(FileName))
                return false;
            var ext = Path.GetExtension(FileName).Length > 2
                ? Path.GetExtension(FileName)[1..]
                : Path.GetExtension(FileName);
            return Extensions.Any(x => x == ext);
        }
    }

    #endregion

    private static IEnumerable<string> Extensions
        => new[]
        {
            "bat", "sh", "c", "cpp", "hpp", "h", "hxx", "go", "rs", "rust", "mm", "swift", "cs", "fs", "vb", "vba",
            "java", "jsp", "kt", "dart", "groovy", "lua", "js", "ts", "css", "vue", "html", "py", "py2", "py3", "jl",
            "m", "R", "php", "sql", "xml", "yaml", "json", "xaml", "axaml", "svg", "wxml", "scss", "asm", "cc", "cxx",
            "C", "c++", "applescript", "asp", "aspx", "clj", "e", "el", "erl", "es6", "f", "hs", "lisp", "pas", "perl",
            "php", "pl", "pm", "rb", "scala", "v"
        };

    public (int lineNum, string context) FormatCode()
        => Language switch
        {
            "Python" => FormatPython(),
            "XML" or "XAML" or "AXAML" => FormatXML(),
            _ => Format()
        };

    private (int lineNum, string context) Format()
    {
        var encoding = Encoding.UTF8;
        // 部分IDE对cpp文件按照gbk编码
        if (Language is "C++") encoding = Encoding.GetEncoding("GB2312");
        var sReader = new StreamReader(FilePath, encoding);

        var newSource = new StringBuilder();
        var lineNum = 0;
        var inBlock = false;
        var replaceFlag = false;
        var tempLine = ""; // 用于保存被替换的特殊行代码
        const string REPLACEMENT = "REPLACEMENT_REPLACEMENT\n\r\r";
        while (!sReader.EndOfStream)
        {
            var line = sReader.ReadLine();
            if (string.IsNullOrEmpty(line)) continue; // 去除空行
            while (line.StartsWith(' '))
                line = line.TrimStart(' ');
            while (line.StartsWith('\t'))
                line = line.TrimStart('\t');

            const string quotationPattern = "^(.*?)\".*//.*\"";
            var quotationResult = Regex.Match(line, quotationPattern);
            if (quotationResult.Success)
            {
                tempLine = quotationResult.Groups[0].Value;
                replaceFlag = true;
                line = Regex.Replace(line, quotationPattern, REPLACEMENT);
            }

            // 单行注释
            if (line.Trim().StartsWith("//"))
                continue;
            if (line.Trim().StartsWith("/*") && line.EndsWith("*/"))
                continue;

            // 注释块
            if (Regex.Match(line.Trim(), @"^/\*").Success)
                inBlock = true;
            if (Regex.Match(line.Trim(), @"\*/$").Success)
            {
                inBlock = false;
                continue;
            }

            const string pattern = "^(.*?)//(.*)";
            var result = Regex.Match(line, pattern);
            if (result.Success)
            {
                line = result.Groups[1].Value;
            }

            // 还原被替换的代码
            if (replaceFlag)
            {
                line = line.Replace(REPLACEMENT, tempLine);
                replaceFlag = false;
            }

            if (inBlock || string.IsNullOrEmpty(line)) continue;
            line = line.Replace(Environment.NewLine, "");
            newSource.Append(lineNum == 0 ? line : Environment.NewLine + line);
            lineNum++;
        }

        return (lineNum, newSource.ToString());
    }

    private (int lineNum, string context) FormatPython()
    {
        var sReader = new StreamReader(FilePath);
        var newSource = new StringBuilder();
        var lineNum = 0;
        var replaceFlag = false;
        var tempLine = ""; // 用于保存被替换的特殊行代码
        const string REPLACEMENT = "REPLACEMENT_REPLACEMENT\n\r\r";
        while (!sReader.EndOfStream)
        {
            var line = sReader.ReadLine();
            if (string.IsNullOrEmpty(line)) continue; // 去除空行
            while (line.StartsWith(' '))
                line = line.TrimStart(' ');
            while (line.StartsWith('\t'))
                line = line.TrimStart('\t');

            const string quotationPattern = "^(.*?)\".*//.*\"";
            var quotationResult = Regex.Match(line, quotationPattern);
            if (quotationResult.Success)
            {
                tempLine = quotationResult.Groups[0].Value;
                replaceFlag = true;
                line = Regex.Replace(line, quotationPattern, REPLACEMENT);
            }

            // 单行注释
            if (line.Trim().StartsWith("#"))
                continue;

            const string pattern = "^(.*?)#(.*)";
            var result = Regex.Match(line, pattern);
            if (result.Success)
                line = result.Groups[1].Value;

            // 还原被替换的代码
            if (replaceFlag)
            {
                line = line.Replace(REPLACEMENT, tempLine);
                replaceFlag = false;
            }

            if (string.IsNullOrEmpty(line)) continue;
            line = line.Replace(Environment.NewLine, "");
            newSource.Append(lineNum == 0 ? line : Environment.NewLine + line);
            lineNum++;
        }

        return (lineNum, newSource.ToString());
    }

    private (int lineNum, string context) FormatXML()
    {
        var sReader = new StreamReader(FilePath);

        var newSource = new StringBuilder();
        var lineNum = 0;
        var inBlock = false;
        while (!sReader.EndOfStream)
        {
            var line = sReader.ReadLine();
            if (string.IsNullOrEmpty(line)) continue; // 去除空行
            while (line.StartsWith(' '))
                line = line.TrimStart(' ');
            while (line.StartsWith('\t'))
                line = line.TrimStart('\t');

            // 单行注释
            if (line.Trim().StartsWith("<!--"))
                continue;
            if (line.Trim().StartsWith("<!--") && line.EndsWith("-->"))
                continue;

            const string pattern = "^(.*?)<!--(.*)-->";
            var result = Regex.Match(line, pattern);
            if (result.Success)
                line = result.Groups[1].Value;

            // 注释块
            if (Regex.Match(line.Trim(), @"^<!--").Success)
                inBlock = true;
            if (Regex.Match(line.Trim(), @"-->$").Success)
            {
                inBlock = false;
                continue;
            }

            if (inBlock || string.IsNullOrEmpty(line)) continue;
            line = line.Replace(Environment.NewLine, "");
            newSource.Append(lineNum == 0 ? line : Environment.NewLine + line);
            lineNum++;
        }

        return (lineNum, newSource.ToString());
    }
}

public class GitIgnore
{
    public static GitIgnore Default => new(["bin/", "obj/", "/packages/", "riderModule.iml", "/_ReSharper.Caches/"]);
    public string[] IgnoreList { get; }

    public GitIgnore(IEnumerable<string> ignoreList)
    {
        IgnoreList = ignoreList.Where(x => !string.IsNullOrEmpty(x) || !x.StartsWith('#'))
            .Select(x => x.Replace("/", @"\\")).ToArray();
    }

    public bool IsIgnored(string path)
    {
        if (IgnoreList.Length == 0)
        {
            return false;
        }

        try
        {
            return IgnoreList.Any(ignore => Regex.IsMatch(path, ignore));
        }
        catch
        {
            return false;
        }
    }
}